﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CsvHelperPlus;
using System.Data;
using System.Reflection;
using System.Collections;
using System.Text.RegularExpressions;


namespace MarketDataCheck
{
    public class DataCheckMachine
    {

        private DataTable dt = null;
        public delegate void ErrorEventHandler(List<ErrorInfo> errList);
        public event ErrorEventHandler onCheckErrorEvent;
        public DataCheckMachine(string configPath)
        {
            dt = loadCsv(configPath);
        }

        private DataTable loadCsv(string file)
        {
            DataTable dt = CsvHandler.ReadCSV(file);
            return dt;
        }

        public ErrorInfo BuildErrorInfo(string name, ErrorType errType, string value)
        {
            ErrorInfo err = new ErrorInfo();
            err.fieldName = name;
            err.errType = errType;
            err.value = value;
            return err;
        }

        public bool Check(Object dataObj)
        {
            List<ErrorInfo> ErrList = new List<ErrorInfo>();
            FieldInfo[] fields = dataObj.GetType().GetFields();
            foreach (var field in fields)
            {
                var checkItem = (from DataRow dr in dt.Rows where (string)dr["COLNAME"] == field.Name select new {
                    Rule = (string)dr["RULE"],
                    LowBound = (string)dr["LOWBOUND"],
                    UpBound = (string)dr["UPBOUND"], 
                    Precis = (string)dr["OUTPUTFORMAT"], 
                    DType = (string)dr["DATATYPE"],
                    Count = (string)dr["COUNT"]
                }).FirstOrDefault();
                if (checkItem == null)  //该field不在检测项目中
                {
                    continue;
                }
                dynamic value = field.GetValue(dataObj);
                IterativeCheck(value, checkItem, field.Name, ref ErrList);
            }
            if (ErrList.Count > 0)
            {
                if (onCheckErrorEvent != null)
                {
                    onCheckErrorEvent(ErrList);
                }
                return false;
            }
            return true;
        }

        private void IterativeCheck(dynamic value, dynamic checkItem, string fieldName, ref List<ErrorInfo> ErrList)
        {
            if (value is ICollection)
            {
                if (value.Count != Convert.ToInt32(checkItem.Count))
                {
                    ErrList.Add(BuildErrorInfo(fieldName, ErrorType.ErrorCount, value.Count.ToString()));
                }
                foreach (var val in value)
                {
                    IterativeCheck(val, checkItem, fieldName, ref ErrList);
                }
                    
            }
            else
            {
                if (value == null)
                {
                    ErrList.Add(BuildErrorInfo(fieldName, ErrorType.NullReference, null));
                    return; //因为没值也不用继续检查了
                }
                if (!CheckRule(value, checkItem.Rule))
                {
                    ErrList.Add(BuildErrorInfo(fieldName, ErrorType.UnmatchRule, value.ToString()));
                }
                if (!CheckLowBound(value, checkItem.LowBound))
                {
                    ErrList.Add(BuildErrorInfo(fieldName, ErrorType.BelowBound, value.ToString()));
                }
                if (!CheckUpBound(value, checkItem.UpBound))
                {
                    ErrList.Add(BuildErrorInfo(fieldName, ErrorType.AboveBound, value.ToString()));
                }
            }
        }    


        private bool CheckRule<T>(T checkobj, string rule)
        {
            if (rule == "-")
            {
                //Console.WriteLine("不用检查");
                return true;
            }
            string val = checkobj.ToString();
            foreach (string criter in rule.Split('/'))
            {
                string expr = BuildRegExpr(criter);
                if (Regex.IsMatch(val, expr))
                {
                    return true; 
                }
            }
            return false;
        }

        private string BuildRegExpr(string criter)
        {
            string expr = null;
            if(criter.Contains('*'))
            {
                expr = @"^" + criter.Replace("*",".*"); 
            }
            else
            {
                expr = @"^" + criter + @"$"; //长度匹配
            }
            return expr;
        }


        private bool CheckLowBound<T>(T checkobj, string LowBound) where T : IComparable
        {
            if (LowBound == "-")
            {
                //Console.WriteLine("不用检查");
                return true;
            }
            var val = System.Convert.ChangeType(LowBound, checkobj.GetType());
            return checkobj.CompareTo(val) >= 0;
        }



        private bool CheckUpBound<T>(T checkobj, string UpBound) where T : IComparable
        {
            if (UpBound == "-")
            {
                //Console.WriteLine("不用检查");
                return true;
            }
            var val = System.Convert.ChangeType(UpBound, checkobj.GetType());
            return checkobj.CompareTo(val) <= 0;
        }

        
    }

    public class ErrorInfo
    {
        public string fieldName { get; set; }
        public ErrorType errType { get; set; }
        public string value { get; set; }
    }

    public enum ErrorType
    {
        UnmatchRule = 0,
        AboveBound =1,
        BelowBound = 2,
        WrongPrecis = 3,
        WrongFormat = 4,
        NullReference = 5,
        ErrorCount = 6,
    }
}
